local npc_role = {noone=0, listener=1, director=2} local camp_ini = ini_file("scripts\\camp.ltx") --local story_table = parse_names(camp_ini:r_string_ex("camp", "stories") or "test_story") local story_table = { -- TODO: find why it can't read the parameter "s_story_1", "s_story_2", "s_story_3", "s_story_4", "s_story_5", "s_story_6", "s_story_7", "s_story_8", "s_story_9", "s_story_10", "s_story_11", "test_story" } local guitar_table = parse_names(camp_ini:r_string_ex("camp", "guitar_themes") or "test_guitar") local harmonica_table = parse_names(camp_ini:r_string_ex("camp", "harmonica_themes") or "test_harmonica") local sr_camp_states = { idle = { director_state = nil, general_state = "idle", min_time = 25000, max_time = 120000, transitions = {"harmonica","guitar","story"}, precondition = function(camp) return true end }, harmonica = { director_state = "play_harmonica", general_state = "listen", transitions = {"idle"}, precondition = function (camp) if(#harmonica_table>0) then for id,v in pairs(camp.npc) do local st = db.storage[id] local npc = st and st.object if (npc) then local scheme = st and st.active_scheme and st[st.active_scheme] if(v.harmonica==npc_role.director) and (scheme ~= nil and scheme.base_action == scheme.description) and not xr_meet.is_meet(npc) then return true end end end end return false end }, guitar = { director_state = "play_guitar", general_state = "listen", transitions = {"idle"}, precondition = function (camp) if(#guitar_table>0) then for id,v in pairs(camp.npc) do local st = db.storage[id] local npc = st and st.object if (npc) then local scheme = st and st.active_scheme and st[st.active_scheme] if(v.guitar==npc_role.director) and (scheme ~= nil and scheme.base_action == scheme.description) and not xr_meet.is_meet(npc) then return true end end end end return false end }, story = { director_state = "tell", general_state = "listen", timeout = 5000, transitions = {"idle"}, precondition = function (camp) if (#story_table>0) then for id,v in pairs(camp.npc) do local st = db.storage[id] local npc = st and st.object if (npc) then local scheme = st and st.active_scheme and st[st.active_scheme] if (v.story==npc_role.director) then if (scheme == nil or scheme.base_action ~= scheme.description) or (xr_meet.is_meet(npc)) then return false end else return true end end end return true end return false end } } -------------------------------------------------------------------------------- -- Класс CCampManager -------------------------------------------------------------------------------- -- Кемп содержит свои действия в терминах состояния кемпа (рассказ истории, музыка, ожидание), а не в терминах анимаций персонажей -- Кемп возвращает свое состояние, а решение какую именно анимацию играть принимает финальная схема, например xr_animpoint class "CCampManager" function CCampManager:__init(object) self.object = object self.npc = {} self.npc_count = 0 self.schemes = {} -- Хранилище для режиссера лагеря. Режиссерем является сталкер, затеявший необычное поведение self.director = nil -- Текущее состояние self.active_state = "idle" --' Для теста создаем объект истории self.sound_manager = sound_manager.get_sound_manager("camp"..self.object:id()) self.sound_manager_started = true -- Состояния кемпа self.active_state_time = 0 self.timeout = 0 self.idle_talker = nil end -- Переключает состояния кемпа function CCampManager:update() if (self.npc_count == 0) then return end -- Если саундменеджер говорит какую то историю - ждем окончания. if not self.sound_manager:is_finished() then self.sound_manager:update() return end if (self.sound_manager_started == false) then if (self.timeout ~= 0 and time_global() > self.timeout) then self:set_story() self.timeout = 0 end return end -- Если кто-то говорит айдловую фразу, ждем пока он договорит. if (self.idle_talker ~= nil) then if (xr_sound.sound_table[self.idle_talker]) then return else self.idle_talker = nil end end -- Выбор состояния кемпа. Тут решается что делать дальше. --printf("camp time %s", tostring(self.active_state_time - time_global())) if (self.active_state_time < time_global()) then self:set_next_state() if not self:get_director() then self:set_next_state("idle") end self.sound_manager_started = false for k,v in pairs(self.npc) do if(db.storage[k]) then xr_logic.issue_event(db.storage[k].object, db.storage[k][db.storage[k].active_scheme], "update") end local meet = db.storage[k] and db.storage[k].meet and db.storage[k].meet.meet_manager if meet then meet.npc_is_camp_director = self.director == k end end --printf("%s [%s]",self.object:name(),self.active_state) end -- Болтовня в айдле if (self.active_state=="idle") then local npc_count = 0 local talkers = {} for k,v in pairs(self.npc) do npc_count = npc_count + 1 talkers[npc_count] = k end if(npc_count > 0) then self.idle_talker = talkers[math.random(npc_count)] xr_sound.set_sound_play(self.idle_talker, "state") end end end function CCampManager:set_next_state(new_state) -- altered to just be completely random choice, the old way just skews chance to whatever was first in table local transitions = sr_camp_states[self.active_state].transitions new_state = new_state or transitions[math.random(#transitions)] -- We could technically check precondition for each state before choosing, -- but it is very likely if one precondition fails the others will too (xr_meet) so it would be a waste self.active_state = sr_camp_states[new_state].precondition(self) and new_state or "idle" self.active_state_time = time_global()+math.random(sr_camp_states[self.active_state].min_time or 0, sr_camp_states[self.active_state].max_time or 1) local timeout = sr_camp_states[self.active_state].timeout or 0 self.timeout = time_global() + timeout end function CCampManager:get_director() if (self.active_state=="idle") then self.director = nil return end local directors = {} local npc_count = 0 for k,v in pairs(self.npc) do local storage = db.storage[k] if storage ~= nil then local scheme = storage.active_scheme and storage[storage.active_scheme] local npc = storage.object if not (xr_meet.is_meet(npc)) then if (v[self.active_state]==npc_role.director) then if (scheme and scheme.base_action == scheme.description) then table.insert(directors, k) end end end end end if (#directors == 0) then self.director = nil return end self.director = directors[math.random(#directors)] return self.director end function CCampManager:set_story() if(self.active_state=="story") then self.sound_manager:set_storyteller(self.director) self.sound_manager:set_story(story_table[math.random(#story_table)]) self.sound_manager_started = true self.sound_manager:update() elseif(self.active_state=="idle") then self.sound_manager_started = true end end -- Возвращает текущее действие кемпа function CCampManager:get_camp_action(npc_id) if(npc_id==nil) then printf("Trying to use destroyed object!") end if(self.npc[npc_id]==nil) then return end return self.active_state, self.director==npc_id end -- Регистрит персонажа в кемп function CCampManager:register_npc(npc_id) -- local npc_id = npc:id() --printf("Register NPC to camp %s", self.object:id()) self.npc[npc_id] = {} self.npc_count = self.npc_count + 1 db.storage[npc_id].registred_camp = self.object:id() -- При регистрации персонажа, проверяем какие роли он может выполнять. for k,v in pairs(sr_camp_states) do local role = self:get_npc_role(npc_id, k) if(role==npc_role.noone) then local se_obj = npc_id and alife_object(npc_id) printf("Wrong role for npc[%s] with id[%d] in camp [%s]!!!", se_obj and se_obj:name(), npc_id, self.object:name()) end self.npc[npc_id][k] = role end self.sound_manager:register_npc(npc_id) xr_logic.issue_event(db.storage[npc_id].object, db.storage[npc_id][db.storage[npc_id].active_scheme], "update") end -- Убирает персонажа из кемпа function CCampManager:unregister_npc(npc_id) -- Если удаляется режиссер лагеря if self.director == npc_id then self.sound_manager_started = false self.active_state_time = 0 self.director = nil self.active_state = "idle" end db.storage[npc_id].registred_camp = nil self.npc[npc_id] = nil self.npc_count = self.npc_count - 1 self.sound_manager:unregister_npc(npc_id) end function CCampManager:get_npc_role(npc_id, state) local st = db.storage[npc_id] local scheme = st and st.active_scheme and st[st.active_scheme] if scheme == nil then return npc_role.noone end local npc_actions = scheme.approved_actions if not (npc_actions) then return npc_role.noone end local descr = scheme.description if(state=="harmonica") or (state=="guitar") then descr = descr and descr.."_"..state or state for i = 1,#npc_actions do --printf("check action [%s]", npc_actions[i].name) if(npc_actions[i].name==descr) then return npc_role.director end end return npc_role.listener elseif(state=="story") then for i = 1,#npc_actions do if(npc_actions[i].name==descr) or (npc_actions[i].name==descr.."_weapon") then --printf("DIRECTOR FOUND story") return npc_role.director end end return npc_role.listener elseif(state=="idle") then return npc_role.listener end return npc_role.noone end -------------------------------------------------------------------------------- function get_current_camp(position) for k,v in pairs(bind_camp.camps) do if v.object:inside(position) then return v.camp end end return nil end function start_guitar(npc) local camp_id = db.storage[npc:id()].registred_camp if camp_id == nil then return end local camp = bind_camp.camps[camp_id].camp camp.sound_manager:set_storyteller(camp.director) camp.sound_manager:set_story(guitar_table[math.random(#guitar_table)]) camp.sound_manager_started = true camp.sound_manager:update() end function start_harmonica(npc) local camp_id = db.storage[npc:id()].registred_camp if camp_id == nil then return end local camp = bind_camp.camps[camp_id].camp camp.sound_manager:set_storyteller(camp.director) camp.sound_manager:set_story(harmonica_table[math.random(#harmonica_table)]) camp.sound_manager_started = true camp.sound_manager:update() end